/***************************************************************************
 *   Copyright (C) 2009 - 2010 by Simon Qian <SimonQian@SimonQian.com>     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "vsf.h"

// fb_stream
static void vsf_fbstream_init(struct vsf_stream_t *stream)
{
	struct vsfui_fb_stream_t *fbstream = (struct vsfui_fb_stream_t *)stream;
	vsf_multibuf_init(&fbstream->multibuf);
}

static uint32_t vsf_fbstream_get_data_length(struct vsf_stream_t *stream)
{
	struct vsfui_fb_stream_t *fbstream = (struct vsfui_fb_stream_t *)stream;
	return fbstream->multibuf.length * fbstream->multibuf.size;
}

static uint32_t vsf_fbstream_get_avail_length(struct vsf_stream_t *stream)
{
	struct vsfui_fb_stream_t *fbstream = (struct vsfui_fb_stream_t *)stream;
	return (fbstream->multibuf.count - fbstream->multibuf.length) *
			fbstream->multibuf.size;
}

static uint32_t
vsf_fbstream_get_wbuf(struct vsf_stream_t *stream, uint8_t **ptr)
{
	struct vsfui_fb_stream_t *fbstream = (struct vsfui_fb_stream_t *)stream;
	uint8_t *buf = vsf_multibuf_get_empty(&fbstream->multibuf);

	if (ptr) *ptr = buf;
	return buf ? fbstream->multibuf.size : 0;
}

static uint32_t
vsf_fbstream_get_rbuf(struct vsf_stream_t *stream, uint8_t **ptr)
{
	struct vsfui_fb_stream_t *fbstream = (struct vsfui_fb_stream_t *)stream;
	uint8_t *buf = vsf_multibuf_get_payload(&fbstream->multibuf);

	if (ptr) *ptr = buf;
	return buf ? fbstream->multibuf.size : 0;
}

static uint32_t
vsf_fbstream_write(struct vsf_stream_t *stream, struct vsf_buffer_t *buffer)
{
	struct vsfui_fb_stream_t *fbstream = (struct vsfui_fb_stream_t *)stream;
	uint8_t *buf = vsf_multibuf_get_empty(&fbstream->multibuf);

	if (buf != NULL) vsf_multibuf_push(&fbstream->multibuf);
	return buf ? fbstream->multibuf.size : 0;
}

static uint32_t
vsf_fbstream_read(struct vsf_stream_t *stream, struct vsf_buffer_t *buffer)
{
	struct vsfui_fb_stream_t *fbstream = (struct vsfui_fb_stream_t *)stream;
	uint8_t *buf = vsf_multibuf_get_payload(&fbstream->multibuf);

	if (buf != NULL) vsf_multibuf_pop(&fbstream->multibuf);
	return buf ? fbstream->multibuf.size : 0;
}

static const struct vsf_stream_op_t vsf_fbstream_op =
{
	.init = vsf_fbstream_init,
	.fini = vsf_fbstream_init,
	.write = vsf_fbstream_write,
	.read = vsf_fbstream_read,
	.get_data_length = vsf_fbstream_get_data_length,
	.get_avail_length = vsf_fbstream_get_avail_length,
	.get_wbuf = vsf_fbstream_get_wbuf,
	.get_rbuf = vsf_fbstream_get_rbuf,
};

static struct vsfsm_state_t *
vsfui_fb_evthandler(struct vsfsm_t *sm, vsfsm_evt_t evt)
{
	struct vsfui_fb_t *fb = (struct vsfui_fb_t *)sm->user_data;

	switch (evt)
	{
	case VSFSM_EVT_INIT:
		if (fb->screen.refresh_interval > 0)
			vsftimer_create(sm, fb->screen.refresh_interval, -1, VSFSM_EVT_TIMER);
		break;
	case VSFSM_EVT_TIMER:
		vsfui_fb_refresh(fb);
		break;
	}
	return NULL;
}

vsf_err_t vsfui_fb_init(struct vsfui_fb_t *fb, uint8_t **buflist, uint16_t cnt)
{
	fb->mbuf.multibuf.size =
			fb->screen.height * fb->screen.width * fb->screen.pixel_bitsize / 8;
	fb->mbuf.multibuf.buffer_list = buflist;
	fb->mbuf.multibuf.count = cnt;
	fb->mbuf.stream.op = &vsf_fbstream_op;
	VSFSTREAM_INIT(&fb->mbuf);
	VSFSTREAM_CONNECT_TX(&fb->mbuf);
	fb->av_stream.stream = &fb->mbuf.stream;
	fb->locked = true;

	if (fb->screen.refresh_interval > 0)
	{
		fb->sm.init_state.evt_handler = vsfui_fb_evthandler;
		fb->sm.user_data = fb;
		return vsfsm_init(&fb->sm);
	}
	return VSFERR_NONE;
}

vsf_err_t vsfui_fb_fini(struct vsfui_fb_t *fb)
{
	return fb->screen.refresh_interval > 0 ? vsfsm_fini(&fb->sm) : VSFERR_NONE;
}

vsf_err_t vsfui_fb_refresh(struct vsfui_fb_t *fb)
{
	uint32_t extrabuf = fb->screen.copy ? 1 : 0;

	if (!fb->locked &&
		(fb->mbuf.multibuf.count > (fb->mbuf.multibuf.length + extrabuf)))
	{
		uint8_t *origbuf = vsf_multibuf_get_empty(&fb->mbuf.multibuf);
		if (VSFSTREAM_WRITE(&fb->mbuf, NULL))
		{
			if (fb->screen.copy)
			{
				uint8_t *buf = vsf_multibuf_get_empty(&fb->mbuf.multibuf);
				memcpy(buf, origbuf, fb->mbuf.multibuf.size);
			}
			vsfsm_sem_post(&fb->av_stream.frame_sync);
			return VSFERR_NONE;
		}
	}
	return VSFERR_FAIL;
}

void vsfui_fb_lock(struct vsfui_fb_t *fb)
{
	uint8_t orig_level = vsfsm_sched_lock();
	fb->locked = true;
	vsfsm_sched_unlock(orig_level);
}

void vsfui_fb_unlock(struct vsfui_fb_t *fb)
{
	fb->locked = false;
}

uint8_t *vsfui_fb_get_frame(struct vsfui_fb_t *fb)
{
	uint8_t *buff;
	uint32_t size = VSFSTREAM_GET_WBUF(&fb->mbuf, &buff);
	return size > 0 ? buff : NULL;
}
